#ifndef _tenacitas__balance_line_h_
#define _tenacitas__balance_line_h_

// =============================================================================
/** @file */

// =============================================================================
/** todo */

// =============================================================================
//                                 C++ Headers
#include <memory>
#include <list>
#include <utility>

// =============================================================================
//                                 3rds Headers


// =============================================================================
//                                 Our Headers

#ifdef DEBUG
#include <tenacitas_text_log_file/logger.h>
#endif

// =============================================================================
//                                 Namespaces
using namespace std;


// =============================================================================
//                                 Macro Contants
/** */


// =============================================================================
//                                 Macro Commands
/** */


/** namespace of the organization */
namespace tenacitas {
    /** namespace for the project */
    namespace balance_line {

        // =========================== Typedefs ===========================
        /** */
#ifdef DEBUG
        typedef text_log_file::logger logger;
#endif

        // ============================ Pre-Declarations ==================

        // ============================ Attributes ========================
        /** */

        // ============================ Functions =========================
        /** */

        // ============================ Classes ===========================
        /**


           @tparam t_source is type which contais a collection of data to be
           merged. It must implement:
           @code

           typedef ptr

           typedef data

           typedef data_ptr

           data_ptr read();

           @endcode

           t_source::data must implement:
           @code

           bool operator <
           @endcode

           @nosubgrouping
        */
        template <typename t_source>
        class merger {

        public:

            // --- merger - Public Internal Types ------------------------

            /** */
            typedef std::shared_ptr<merger> ptr;

            /** */
            typedef t_source source;

            /** */
            typedef typename source::ptr source_ptr;

            /** */
            typedef typename source::data data;

            /** */
            typedef typename source::data_ptr data_ptr;

            // --- merger - Public Constructors --------------------------

            /**
             */
            static ptr create( ) {

#ifdef DEBUG
                logger l_log;
                logger::set_level_all( tenacitas::log::level::trace );
                log_trace( l_log ) << "creating the merger";
#endif
                return ptr(new merger());
            }

            /**
             */
            merger( const merger & ) = delete;

            /**
             */
            merger( merger && ) = delete;

            /** Destructor */
            ~merger( ) {}            

            // --- merger - Public Accessors -----------------------------

            /**
               @param [in] p_source is one more data source to be merged
            */
            void add( source_ptr p_source ) {

#ifdef DEBUG
                logger l_log;
                log_trace( l_log ) << "adding source";
#endif
                // we only add this source if it contains data 
                data_ptr l_data = p_source->read();
                if ( l_data ) {
                    // and this make it easier to calculate the smallest data
                    // later

#ifdef DEBUG
                    log_trace( l_log ) <<  "source has data";
#endif
                    
                    m_sources_data.push_back( std::make_pair( p_source,
                                                              l_data ) ); 
                }
            }

            // --- merger - Public Processors ----------------------------

            /**

               @tparam t_output is the class that will handled each data merged
               from the sources. It must implement:
               @code
               void add( data_ptr )
               @endcode

               @param [in] p_output will handled each data merged from the sources
            */
            template <typename t_output>
            void start( t_output & p_output ) {
#ifdef DEBUG
                logger l_log;
                log_trace( l_log ) << "starting";
#endif

                while( true ) {
                    if ( m_sources_data.empty( ) ) {
                        break;
                    }

#ifdef DEBUG
                    log_trace( l_log ) << "still data to process";
#endif
                    
                    data_ptr l_smallest = calculate_smallest_data();
                    if ( l_smallest ) {

#ifdef DEBUG
                        log_trace( l_log ) << "a new smallest found: "
                                           << *l_smallest;
#endif
                        p_output.add( l_smallest );
                    }

                    read_data( );
                }
            }

            // --- merger - Public Operators -----------------------------

            /** not-allowed */
            merger & operator =( const merger & ) = delete;

            /** not-allowed */
            merger & operator =( merger && ) = delete;

            /** not-allowed */
            bool operator ==( const merger & ) const = delete;

            /** not-allowed */
            bool operator !=( const merger & ) const = delete;

            // --- merger - Public Attributes ----------------------------

        private:

            // --- merger - Private Internal Types -----------------------

            /**
             */
            typedef std::pair<source_ptr, data_ptr> source_and_data;

            /**
             */
            typedef list<source_and_data> sources_and_data;

            /** */
            typedef typename sources_and_data::iterator iterator;

            /** */
            typedef typename sources_and_data::const_iterator const_iterator;
            

            // --- merger - Private Constructors -------------------------

            /**
             */
            merger()
                : m_sources_data(),
                  m_smallest( m_sources_data.end() ) {}

            // --- merger - Private Processors ---------------------------

            /** Updates the data from the source that had the last smallest data
             */
            void read_data() {

#ifdef DEBUG                
                logger l_log;
                log_trace( l_log ) << "updating data from the last "
                    "smallest source";
#endif

                // reading a new data from the source that had the previous
                // smallest data

                m_smallest->second = m_smallest->first->read();

                if ( !m_smallest->second ) {
                    m_sources_data.erase( m_smallest );
                }
            }

            /**
             */
            data_ptr calculate_smallest_data() {


                m_smallest = m_sources_data.begin();

#ifdef DEBUG                
                logger l_log;
                log_trace( l_log ) << "entering with smallest's source = "
                                   << m_smallest->first->name( );
#endif

                iterator l_end = m_sources_data.end();
                iterator l_ite = ++( m_sources_data.begin() );
                
                for( ; l_ite != l_end; ++l_ite ) {
#ifdef DEBUG
                    log_trace( l_log )
                        << "current smallest = " << *( m_smallest->second ) << ", "
                        << "possible new = " << *( l_ite->second );
#endif
                    
                    if ( ( *l_ite->second ) < ( *m_smallest->second ) ) {
                        m_smallest = l_ite;
#ifdef DEBUG
                        log_trace( l_log ) << "found a new smallest data: "
                                           << *( m_smallest->second );
#endif
                    }
                }

#ifdef DEBUG
                log_trace( l_log ) << "leaving with smallest's source = "
                                   << m_smallest->first->name( );
#endif
                return m_smallest->second;
            }

            // --- merger - Private Attributes ---------------------------

            /**
             */
            sources_and_data m_sources_data;

            /**
             */
            iterator m_smallest;

        };
    }
}

// =============================================================================
//                     I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::balance_line;


#endif
